package com.osfw.framework.config.orika;

import com.osfw.framework.annotation.FieldMapping;
import com.osfw.framework.annotation.ModelMapping;
import com.osfw.framework.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.CustomMapper;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingException;
import ma.glasnost.orika.PropertyNotFoundException;
import ma.glasnost.orika.metadata.ClassMapBuilder;
import org.reflections.Reflections;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Set;

/**
 * 对象字段映射自动配置类
 */
@Slf4j
@Component
public class BeanMapperRegistry implements IBeanMapperRegistry {
    /* 映射关系对象注解扫描包路径 */
    private final static String SCAN_PACKAGE = "com.osfw.*.*.domain,com.osfw.*.*.entity";

    /**
     * 扫包下指定配置了映射关系的类，并将其映射关系进行注册
     *
     * @param mapperFactory 映射工厂
     */
    @Override
    public void registry(MapperFactory mapperFactory) {
        // Map<String, Object> --> Object 或 Object --> Object
        if (StringUtils.isNotEmpty(SCAN_PACKAGE)) {
            Arrays.stream(SCAN_PACKAGE.replaceAll(", ", ",").split(",")).forEach(x -> {
                Reflections reflections = new Reflections(x);
                // 目标注解类
                Set<Class<?>> set = reflections.getTypesAnnotatedWith(ModelMapping.class);
                if (set != null) {
                    set.stream().forEach(clz -> {
                        ModelMapping modelAnn = clz.getAnnotation(ModelMapping.class);
                        if (modelAnn == null) {
                            return;
                        }
                        ClassMapBuilder<?, ?> mappingCmb = null;

                        //对象映射
                        if (modelAnn.mappingClass() != null && modelAnn.mappingClass() != Class.class) {
                            mappingCmb = mapperFactory.classMap(modelAnn.mappingClass(), clz);
                        } else {
                            return;
                        }

                        //字段映射
                        Field[] fields = clz.getDeclaredFields();
                        ClassMapBuilder<?, ?> finalMappingCmb = mappingCmb;
                        Arrays.stream(fields).forEach(field -> {
                            try {
                                if (field.isAnnotationPresent(FieldMapping.class)) {
                                    FieldMapping fieldAnnotation = field.getAnnotation(FieldMapping.class);
                                    if (fieldAnnotation != null && fieldAnnotation.name().length() > 0 && finalMappingCmb != null) {
                                        String mappingField = fieldAnnotation.name();
                                        finalMappingCmb.field(mappingField, field.getName());
                                    }
                                }
                            } catch (Exception e) {
                                if (e instanceof PropertyNotFoundException || e instanceof MappingException || e instanceof BeanCreationException) {
                                    log.error("类[" + clz.getSimpleName() + "]属性[" + field.getName() + "]，存在驼峰命名不规范，请调整！！！");
                                } else {
                                    throw e;
                                }
                            }

                        });

                        //自定义映射
                        Method[] methods = clz.getDeclaredMethods();
                        if (methods != null && methods.length > 0 && finalMappingCmb != null) {
                            Arrays.stream(methods).filter(method -> method.getName().equals("customMapper")).forEach(method -> {
                                try {
                                    method.setAccessible(true);
                                    CustomMapper customMapper = (CustomMapper) method.invoke(clz.newInstance());
                                    finalMappingCmb.customize(customMapper);
                                } catch (InvocationTargetException e) {
                                    e.printStackTrace();
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                } catch (InstantiationException e) {
                                    e.printStackTrace();
                                }
                            });
                        }

                        //映射配置注册生效
                        if (mappingCmb != null) {
                            mappingCmb.byDefault().register();
                        }
                    });
                }
            });
        }
    }

}